package uk.ac.aber.paws.server.db;

import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Vector;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;

import uk.ac.aber.paws.client.db.DBCall;
import uk.ac.aber.paws.server.config.ConfigManager;
import uk.ac.aber.paws.server.config.Constants;
import uk.ac.aber.paws.server.core.PAWSServer;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 21st May 2012
 * 
 * DBRemoteCallQueue.java
 * 
 * This is a queuer for DBCalls that are sent by clients. DB Calls are queued and processed in order. This
 * class allows for the same database connection to process multiple database calls, sent by the many clients.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class DBRemoteCallQueue extends ParentDBOps {

	private boolean running = true;
	private LinkedBlockingDeque<DBCall> callQueue = new LinkedBlockingDeque<DBCall>();

	public DBRemoteCallQueue() {
		this.start();
	}
	
	public int getQueueSize() {
		return callQueue.size();
	}
	
	public String toString() {
		
		return callQueue.toString();
	}

	/**
	 * Starts the poller to continually process the calls present in the queue
	 */
	public void run() {
	
		if (conn == null) {
			PAWSServer.LOG_FILE.log(Level.ALL,"Reconnecting database...");
			connectDB(new DBConnectionObject(Constants.SQLSERVERDB,ConfigManager.getProperty("app.db.principal"),ConfigManager.getProperty("app.db.mirror"),ConfigManager.getProperty("app.db.name"),ConfigManager.getProperty("app.db.username"),ConfigManager.getProperty("app.db.password"),Integer.parseInt(ConfigManager.getProperty("app.db.sqlserverport"))));
		}
		
		Vector<DBCall> processList = new Vector<DBCall>();
		
		while (running) {
		
			Runtime runtime = Runtime.getRuntime();
	        
			try {
				
				// check if the database connection is still valid
				if (!conn.isValid(0)) {
					
					// if not....reopen it
					connectDB(new DBConnectionObject(Constants.SQLSERVERDB,ConfigManager.getProperty("app.db.principal"),ConfigManager.getProperty("app.db.mirror"),ConfigManager.getProperty("app.db.name"),ConfigManager.getProperty("app.db.username"),ConfigManager.getProperty("app.db.password"),Integer.parseInt(ConfigManager.getProperty("app.db.sqlserverport"))));

				}
				else {
					
					processList.clear();
					
					// parse the queue
					callQueue.drainTo(processList,1000);
				
					for (int i=0;i<processList.size();i++) {
						
						// process each DBCall here...
						// if failed, add back to the queue for next time
						if (!DBCallProcessor.executeCall(conn,processList.get(i))) {
							callQueue.offer(processList.get(i));
						}
						else {
							if (((DBCall)processList.get(i)).isSocketValid()) {
								Socket replySocket = ((DBCall)processList.get(i)).getReplySocket();
								
								try {
									ObjectOutputStream oos = new ObjectOutputStream(replySocket.getOutputStream());
									
									if (((DBCall)processList.get(i)).getResponseRequired()) {
										oos.writeObject((DBCall)processList.get(i));
									}
									
									replySocket.close();
								}
								catch (SocketException ex) {
									System.out.println("Error calling: "+((DBCall)processList.get(i)).getStoredProc()+((DBCall)processList.get(i)).getParamList().toString());
									replySocket.close();
								}
							}
						}
					}
				}
			
			}
			catch (Exception ex) {
				ex.printStackTrace();
			}
			
			try {
				this.sleep(100);
			}
			catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}
	}
	
	/**
	 * Returns the current size of the DBcall queue
	 */
	public int size() {
		return callQueue.size(); 
	}
	
	/**
	 * Stops the queue
	 */
	public void stopQueueProcess() {
		
		running = false;
	}
	
	/**
	 * Adds a DBCall to the queue
	 * @param object
	 */
	public boolean addToQueue(DBCall object, boolean priority) {
			
		try {
			if (priority) {
				callQueue.addFirst(object);
			}
			else {
				callQueue.addLast(object);
			}
			
			return true;
		}
		catch (Exception ex) {
			return false;
		}
	
	}

}
